package com.renke.enml;

import java.lang.reflect.InvocationTargetException;
import java.util.List;

import org.codehaus.jparsec.Parser;
import org.codehaus.jparsec.Parser.Reference;
import org.codehaus.jparsec.Parsers;
import org.codehaus.jparsec.Scanners;
import org.codehaus.jparsec.functors.Pair;
import org.codehaus.jparsec.pattern.Patterns;

public class ENMLParser {
	private static final String NESTABLE_IDENTIFIER_PATTERN = "([a-zA-Z_$][a-zA-Z\\d_$]*\\.)*[a-zA-Z_$][a-zA-Z\\d_$]*";
	private static final String IDENTIFIER_PATTERN = "[a-zA-Z_$][a-zA-Z\\d_$]*";
	private static final String ADDITION = "[\\+\\-]";
	private static final String MULTIPLICATION = "[\\*/%]";
	private static final String RELATION = "=|!=|>|>=|<|<=";
	private static final String COMPARATION = "and|or";

	public static ParsedElement parse(String value) {
		return null;
	}

	public static Parser<Void> ignored() {
		return Parsers.or(Scanners.WHITESPACES, Scanners.JAVA_BLOCK_COMMENT, Scanners.JAVA_LINE_COMMENT).skipMany();
	}

	public static Parser<String> identifier(String expected) {
		return ws(Scanners.pattern(Patterns.regex(IDENTIFIER_PATTERN), expected).source());
	}

	public static Parser<String> nestableIdentifier(String expected) {
		return ws(Scanners.pattern(Patterns.regex(NESTABLE_IDENTIFIER_PATTERN), expected).source());
	}

	public static Parser<Variable> variable() {
		return ws(Scanners.string("@").next(nestableIdentifier("variable definition")).source().map(construct(Variable.class)));
	}

	public static Parser<Constant> constant() {
		return ws(Scanners.string("@@").next(identifier("constant definition")).source().map(construct(Constant.class)));
	}

	public static Parser<StringLiteral> string() {
		return ws(Parsers.or(Scanners.SINGLE_QUOTE_STRING, Scanners.DOUBLE_QUOTE_STRING).map(construct(StringLiteral.class)));
	}

	public static Parser<NumberLiteral> number() {
		return ws(Scanners.DECIMAL).map(construct(NumberLiteral.class));
	}

	public static Parser<Term> term() {
		return ws(Parsers.or(number(), string(), variable(), constant())).map(new Converter<Expression, Term>() {
			@Override
			public Term map(Expression from) {
				return new Term(from);
			}
		});
	}

	public static Parser<Negation> negation() {
		return ws(Parsers.pair(Scanners.string("!").source().many(), term())).map(new Converter<Pair<List<String>, Term>, Negation>() {
			@Override
			public Negation map(Pair<List<String>, Term> from) {
				return new Negation(from.a.size() % 2 == 1, from.b);
			}
		});
	}

	public static Parser<Unary> unary() {
		return ws(Parsers.pair(Scanners.pattern(Patterns.regex(ADDITION), "+ -").source(), negation())).map(new Converter<Pair<String, Negation>, Unary>() {
			@Override
			public Unary map(Pair<String, Negation> from) {
				return new Unary(from.b, from.a);
			}
		});
	}

	public static Parser<Multiplication> multiplication() {
		return ws(Parsers.pair(unary(), Parsers.pair(Scanners.pattern(Patterns.regex(MULTIPLICATION), "/ * %").source(), unary()).many())).map(
				new Converter<Pair<Unary, List<Pair<String, Unary>>>, Multiplication>() {
					@Override
					public Multiplication map(Pair<Unary, List<Pair<String, Unary>>> from) {
						return make(from.a, from.b);
					}

					private Multiplication make(Unary expression, List<Pair<String, Unary>> remaining) {
						if (remaining.isEmpty()) {
							return new Multiplication("", expression, null);
						}
						Pair<String, Unary> current = remaining.remove(0);
						return new Multiplication(current.a, expression, make(current.b, remaining));
					}
				});
	}

	public static Parser<Addition> addition() {
		return ws(Parsers.pair(multiplication(), Parsers.pair(Scanners.pattern(Patterns.regex(ADDITION), "+ -").source(), multiplication()).many())).map(
				new Converter<Pair<Multiplication, List<Pair<String, Multiplication>>>, Addition>() {
					@Override
					public Addition map(Pair<Multiplication, List<Pair<String, Multiplication>>> from) {
						return make(from.a, from.b);
					}

					private Addition make(Multiplication expression, List<Pair<String, Multiplication>> remaining) {
						if (remaining.isEmpty()) {
							return new Addition("", expression, null);
						}
						Pair<String, Multiplication> current = remaining.remove(0);
						return new Addition(current.a, expression, make(current.b, remaining));
					}
				});
	}

	public static Parser<Relation> relation() {
		return ws(Parsers.pair(addition(), Parsers.pair(Scanners.pattern(Patterns.regex(RELATION), "= != > >= < <=").source(), addition()).many())).map(
				new Converter<Pair<Addition, List<Pair<String, Addition>>>, Relation>() {
					@Override
					public Relation map(Pair<Addition, List<Pair<String, Addition>>> from) {
						return make(from.a, from.b);
					}

					private Relation make(Addition expression, List<Pair<String, Addition>> remaining) {
						if (remaining.isEmpty()) {
							return new Relation("", expression, null);
						}
						Pair<String, Addition> current = remaining.remove(0);
						return new Relation(current.a, expression, make(current.b, remaining));
					}
				});
	}

	public static Parser<Comparation> comparation() {
		return ws(Parsers.pair(relation(), Parsers.pair(Scanners.pattern(Patterns.regex(COMPARATION), "and or").source(), relation()).many())).map(
				new Converter<Pair<Relation, List<Pair<String, Relation>>>, Comparation>() {
					@Override
					public Comparation map(Pair<Relation, List<Pair<String, Relation>>> from) {
						return make(from.a, from.b);
					}

					private Comparation make(Relation expression, List<Pair<String, Relation>> remaining) {
						if (remaining.isEmpty()) {
							return new Comparation("", expression, null);
						}
						Pair<String, Relation> current = remaining.remove(0);
						return new Comparation(current.a, expression, make(current.b, remaining));
					}
				});
	}

	public static Parser<Expression> expression() {
		Reference<Expression> reference = Parser.newReference();
		return null;
	}

	public static <P> Parser<P> ws(Parser<P> parser) {
		return ignored().optional().next(parser);
	}

	private static final <E extends Expression> Converter<String, E> construct(final Class<E> expressionClass) {
		return new Converter<String, E>() {
			@Override
			public E map(String from) {
				try {
					return expressionClass.getConstructor(String.class).newInstance(from);
				} catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException | NoSuchMethodException
						| SecurityException e) {
					throw new IllegalStateException(e);
				}
			}
		};
	}

	public static void main(String[] args) {
	}
}
